﻿using System;
using System.Diagnostics.CodeAnalysis;
using DimensionsHelper.Common.Contracts.Metadata;

namespace DimensionsHelper.Services.Metadata;

public class MetadataText : IMetadataText
{
    internal MetadataText(IMetadataDocument document, string key, string text, bool cData)
    {
        Document = document;
        Text = text;
        CData = cData;
        Context = string.Empty;
        LanguageName = string.Empty;
        SetContextLanguageNameFromKey(key);
    }

    public IMetadataDocument Document { get; }

    public bool CData { get; internal set; }

    public string Text { get; internal set; }

    public string Context { get; internal set; }

    public ILanguage? Language => Document.Languages.GetByKey(LanguageName);

    public string LanguageName { get; set; }

    internal static string GetKey(string context, string language)
    {
        return $"C:{context}-L:{language}";
    }

    [MemberNotNull(nameof(Context), nameof(LanguageName))]
    private void SetContextLanguageNameFromKey(string key)
    {
        var langIndex = key.IndexOf("-L:", StringComparison.Ordinal);

        if (langIndex == -1 || langIndex <= 3 || langIndex == key.Length - 3)
        {
            throw new Exception($"键'{key}'无效。");
        }

        Context = key[2..langIndex];
        LanguageName = key[(langIndex + 3)..];
    }
}


public class MetadataLabel(IMetadataDocument document) : MetadataStringKeyCollection<IMetadataText>, IMetadataLabel
{
    public string Context { get; internal set; } = "Label";

    public bool IsEmpty => Count == 0;

    public override IMetadataText CreateObject(string key)
    {
        return new MetadataText(Document, key, "", false);
    }

    public string? GetText()
    {
        return GetText(Document.Context, Document.Language);
    }

    public string? GetText(string context, string language)
    {
        var key = MetadataText.GetKey(context, language);
        return TryGetByKey(key, out var label) ? label.Text : null;
    }

    public void SetText(string text)
    {
        SetText(Document.Context, Document.Language, text);
    }

    public void SetText(string context, string language, string text, bool cdata = false)
    {
        var key = MetadataText.GetKey(context, language);
        var textObj = (MetadataText)GetByKey(key);
        textObj.Text = text;
        textObj.CData = cdata;
    }

    public T? FindParent<T>() where T : IMetadataObject
    {
        return MetadataObject.FindParent<T>(this);
    }


    protected override string KeySelector(IMetadataText item)
    {
        return MetadataText.GetKey(item.Context, item.LanguageName);
    }

    #region IMetadataObject

    public IMetadataDocument Document { get; } = document;

    public ObjectType ObjectType => ObjectType.Label;

    public IMetadataObject? Parent { get; internal set; }

    public IVersionObjectCollection? Versions { get; internal set; }

    public bool Versioned { get; set; }

    #endregion
}


public class MetadataLabels : MetadataStringKeyCollection<IMetadataLabel>, IMetadataLabels
{
    internal MetadataLabels(IMetadataDocument document)
    {
        Document = document;
    }

    public IMetadataDocument Document { get; }

    public override IMetadataLabel CreateObject(string key)
    {
        return new MetadataLabel(Document) { Context = key };
    }

    public IMetadataLabel? Get(string labelType)
    {
        return TryGetByKey(labelType, out var label) ? label : null;
    }

    public IMetadataLabel GetOrAdd(string labelType)
    {
        if (TryGetByKey(labelType, out var label))
        {
            return label;
        }

        label = new MetadataLabel(Document) { Context = labelType };
        Add(label);

        return label;
    }

    public void SetText(string labelType, string context, string language, string text, bool cdata = false)
    {
        GetOrAdd(labelType).SetText(context, language, text, cdata);
    }


    public string? GetText()
    {
        return GetText(Document.LabelType, Document.Context, Document.Language);
    }


    public string? GetText(string labelType, string context, string language)
    {
        return Get(labelType)?.GetText(context, language);
    }

    protected override string KeySelector(IMetadataLabel item)
    {
        return item.Context;
    }
}